//
// Created by wukai on 2021/10/21.
//

#include "decode.h"
#include "player.h"
#include <libavutil/pixfmt.h>
#include <unistd.h>
static int img_convert(AVPicture *dst, int dst_pix_fmt, const AVPicture *src,
                       int src_pix_fmt, int src_width, int src_height) {
    int w;
    int h;
    struct SwsContext *pSwsCtx;

    w = src_width;
    h = src_height;

    pSwsCtx = sws_getContext(w, h, (enum AVPixelFormat) src_pix_fmt, w, h,
                             (enum AVPixelFormat) dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
    sws_scale(pSwsCtx, (const uint8_t* const *) src->data, src->linesize, 0, h,
              dst->data, dst->linesize);

    return 0;
}


void* decode_video(void *argv) {
    AVPacket pkt1;
    AVPacket *packet = &pkt1;
    int frameFinished;
    AVFrame *pFrame;
    pFrame = av_frame_alloc();

    for (;;) {
        LOGD("decoding...");
        LOGD("player status %d...",player_status);

        if (player_status == WK_PLAYER_STATUS_STOP || player_status == WK_PLAYER_STATUS_IDLE) {
            LOGD("video_thread need exit. \n");
            break;
        }

        if (player_status == WK_PLAYER_STATUS_PAUSE) {
            LOGD("video_thread WK_PLAYER_STATUS_PAUSE. \n");
            continue;
        }

        if (packet_queue_get(playContext.video_queue, packet) <= 0) {
            // means we quit getting packets
            LOGD("video_thread need more packet. \n");
            continue;
        }


        avcodec_decode_video2(playContext.vcodec_ctx, pFrame, &frameFinished,
                              packet);

        if (frameFinished) {
            AVPicture pict;
            av_image_alloc(pict.data, pict.linesize,
                           playContext.vcodec_ctx->width,
                           playContext.vcodec_ctx->height, AV_PIX_FMT_RGB565LE, 16);

            // Convert the image into YUV format that SDL uses
            img_convert(&pict, AV_PIX_FMT_RGB565LE, (AVPicture *) pFrame,
                        playContext.vcodec_ctx->pix_fmt,
                        playContext.vcodec_ctx->width,
                        playContext.vcodec_ctx->height);

            renderSurface(pict.data[0]);
            LOGD("render_surface....");
            av_freep(&pict.data[0]);
        }

        av_packet_unref(packet);
        av_init_packet(packet);
        usleep(40000);
        av_free(pFrame);
    }

}

void packet_queue_init(struct PacketQueue *q) {
    memset(q, 0, sizeof(struct PacketQueue));
    pthread_mutex_init(q->mutex, NULL);
}

int packet_queue_put(struct PacketQueue *q, AVPacket *pkt) {
    AVPacketList *pkt1;

    if ((NULL == pkt) || (NULL == q)) {
        av_log(NULL, AV_LOG_ERROR,
               "packet_queue_put failure, q or pkt is NULL. \n");
        return -1;
    }

    if (av_dup_packet(pkt) < 0) {
        av_log(NULL, AV_LOG_ERROR, "packet_queue_put av_dup_packet failure.\n");
        return -1;
    }

    pkt1 = (AVPacketList*) av_malloc(sizeof(AVPacketList));
    if (!pkt1) {
        av_log(NULL, AV_LOG_ERROR, "packet_queue_put av_malloc failure.\n");
        return -1;
    }

    pkt1->pkt = *pkt;
    pkt1->next = NULL;

    pthread_mutex_lock(q->mutex);

    if (!q->last_pkt) {
        q->first_pkt = pkt1;
    } else {
        q->last_pkt->next = pkt1;
    }

    q->last_pkt = pkt1;
    q->nb_packets++;
    q->size += pkt1->pkt.size;

    pthread_mutex_unlock(q->mutex);

    return 0;
}

int packet_queue_get(struct PacketQueue *q, AVPacket *pkt) {
    AVPacketList *pkt1;
    int ret;

    if (player_status == WK_PLAYER_STATUS_PAUSE || player_status == WK_PLAYER_STATUS_STOP) {
        return -1;
    }

    pthread_mutex_lock(q->mutex);

    pkt1 = q->first_pkt;

    if (pkt1) {
        q->first_pkt = pkt1->next;

        if (!q->first_pkt) {
            q->last_pkt = NULL;
        }

        q->nb_packets--;
        q->size -= pkt1->pkt.size;
        *pkt = pkt1->pkt;
        av_free(pkt1);
        ret = 1;
    } else {
        ret = 0;
    }

    pthread_mutex_unlock(q->mutex);

    return ret;
}

int packet_queue_size(struct PacketQueue *q) {
    return q->size;
}

